﻿using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Authorization.Users;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Linq.Extensions;
using Abp.Organizations;
using MyCompanyName.AbpZeroTemplate.Authorization;
using MyCompanyName.AbpZeroTemplate.Organizations.Dto;
using System.Collections.Generic;
using System;
using MyCompanyName.AbpZeroTemplate.Gantt;

namespace MyCompanyName.AbpZeroTemplate.Organizations
{
    //[AbpAuthorize(AppPermissions.Pages_Administration_OrganizationUnits)]
    public class OrganizationUnitAppService : AbpZeroTemplateAppServiceBase, IOrganizationUnitAppService
    {
        private readonly OrganizationUnitManager _organizationUnitManager;
        private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;
        private readonly IRepository<UserOrganizationUnit, long> _userOrganizationUnitRepository;
        private readonly IRepository<PushMessage> _pushMessageRepository;

        public OrganizationUnitAppService(
            OrganizationUnitManager organizationUnitManager,
            IRepository<OrganizationUnit, long> organizationUnitRepository,
            IRepository<UserOrganizationUnit, long> userOrganizationUnitRepository,
            IRepository<PushMessage> pushMessageRepository)
        {
            _organizationUnitManager = organizationUnitManager;
            _organizationUnitRepository = organizationUnitRepository;
            _userOrganizationUnitRepository = userOrganizationUnitRepository;
            _pushMessageRepository = pushMessageRepository;
        }

        public async Task<ListResultOutput<OrganizationUnitDto>> GetOrganizationUnits()
        {
            var query =
                from ou in _organizationUnitRepository.GetAll()
                join uou in _userOrganizationUnitRepository.GetAll() on ou.Id equals uou.OrganizationUnitId into g
                select new { ou, memberCount = g.Count() };

            var items = await query.ToListAsync();

            return new ListResultOutput<OrganizationUnitDto>(
                items.Select(item =>
                {
                    var dto = item.ou.MapTo<OrganizationUnitDto>();
                    dto.MemberCount = item.memberCount;
                    return dto;
                }).ToList());
        }

        public async Task<PagedResultOutput<OrganizationUnitUserListDto>> GetOrganizationUnitUsers(GetOrganizationUnitUsersInput input)
        {
            var query = from uou in _userOrganizationUnitRepository.GetAll()
                        join ou in _organizationUnitRepository.GetAll() on uou.OrganizationUnitId equals ou.Id
                        join user in UserManager.Users on uou.UserId equals user.Id
                        where uou.OrganizationUnitId == input.Id
                        orderby input.Sorting
                        select new { uou, user };

            var totalCount = await query.CountAsync();
            var items = await query.PageBy(input).ToListAsync();

            return new PagedResultOutput<OrganizationUnitUserListDto>(
                totalCount,
                items.Select(item =>
                {
                    var dto = item.user.MapTo<OrganizationUnitUserListDto>();
                    dto.AddedTime = item.uou.CreationTime;
                    return dto;
                }).ToList());
        }

        //[AbpAuthorize(AppPermissions.Pages_Administration_OrganizationUnits_ManageOrganizationTree)]
        public async Task<OrganizationUnitDto> CreateOrganizationUnit(CreateOrganizationUnitInput input)
        {
            var organizationUnit = new OrganizationUnit(AbpSession.TenantId, input.DisplayName, input.ParentId);

            await _organizationUnitManager.CreateAsync(organizationUnit);
            await CurrentUnitOfWork.SaveChangesAsync();

            return organizationUnit.MapTo<OrganizationUnitDto>();
        }

        //[AbpAuthorize(AppPermissions.Pages_Administration_OrganizationUnits_ManageOrganizationTree)]
        public async Task<OrganizationUnitDto> UpdateOrganizationUnit(UpdateOrganizationUnitInput input)
        {
            var organizationUnit = await _organizationUnitRepository.GetAsync(input.Id);

            organizationUnit.DisplayName = input.DisplayName;

            await _organizationUnitManager.UpdateAsync(organizationUnit);

            return await CreateOrganizationUnitDto(organizationUnit);
        }

        //[AbpAuthorize(AppPermissions.Pages_Administration_OrganizationUnits_ManageOrganizationTree)]
        public async Task<OrganizationUnitDto> MoveOrganizationUnit(MoveOrganizationUnitInput input)
        {
            await _organizationUnitManager.MoveAsync(input.Id, input.NewParentId);

            return await CreateOrganizationUnitDto(
                await _organizationUnitRepository.GetAsync(input.Id)
                );
        }

        //[AbpAuthorize(AppPermissions.Pages_Administration_OrganizationUnits_ManageOrganizationTree)]
        public async Task DeleteOrganizationUnit(IdInput<long> input)
        {
            await _organizationUnitManager.DeleteAsync(input.Id);
        }

        //[AbpAuthorize(AppPermissions.Pages_Administration_OrganizationUnits_ManageMembers)]
        public async Task AddUserToOrganizationUnit(UserToOrganizationUnitInput input)
        {
            await UserManager.AddToOrganizationUnitAsync(input.UserId, input.OrganizationUnitId);
        }

        //[AbpAuthorize(AppPermissions.Pages_Administration_OrganizationUnits_ManageMembers)]
        public async Task RemoveUserFromOrganizationUnit(UserToOrganizationUnitInput input)
        {
            await UserManager.RemoveFromOrganizationUnitAsync(input.UserId, input.OrganizationUnitId);
        }

        //[AbpAuthorize(AppPermissions.Pages_Administration_OrganizationUnits_ManageMembers)]
        public async Task<bool> IsInOrganizationUnit(UserToOrganizationUnitInput input)
        {
            return await UserManager.IsInOrganizationUnitAsync(input.UserId, input.OrganizationUnitId);
        }

        private async Task<OrganizationUnitDto> CreateOrganizationUnitDto(OrganizationUnit organizationUnit)
        {
            var dto = organizationUnit.MapTo<OrganizationUnitDto>();
            dto.MemberCount = await _userOrganizationUnitRepository.CountAsync(uou => uou.OrganizationUnitId == organizationUnit.Id);
            return dto;
        }


        public async Task<List<OrganizationTreeDto>> GetOrganizationTree()
        {
            var list = await _organizationUnitRepository.GetAllListAsync();

            Dictionary<string, OrganizationTreeDto> res = new Dictionary<string, OrganizationTreeDto>();
            foreach (var item in list)
            {
                res.Add(item.Id.ToString(), new OrganizationTreeDto()
                {
                    id = item.Id.ToString(),
                    text = item.DisplayName,
                    expanded = false,
                    parentId = item.ParentId == null ? "0" : item.ParentId.ToString(),
                    children = new List<OrganizationTreeDto>(),
                    leaf = list.Any(x => x.ParentId == item.Id) ? false : true
                });
            }

            foreach (var item in res.Values)
            {
                if (res.ContainsKey(item.parentId.ToString()))
                {
                    res[item.parentId.ToString()].children.Add(item);
                }
            }

            var results = res.Values.Where(x => x.parentId == "0").ToList();

            return results;
        }


        public List<KeyValuePair<long, string>> GetUsers(long? id, string userName)
        {
            var users = _userOrganizationUnitRepository.GetAll().Where(x => x.OrganizationUnitId == id).Select(x => x.UserId).ToList();
            var results = UserManager.Users.WhereIf(users.Count() > 0, x => users.Contains(x.Id)).WhereIf(!string.IsNullOrEmpty(userName), x => x.Name.Contains(userName)).ToList().Select(x => new KeyValuePair<long, string>(x.Id, x.Name)).ToList();
            return results;
        }



        public List<string> GetUserOrganizations(long userId)
        {

            List<string> lst = new List<string>();
            var ou = _userOrganizationUnitRepository.GetAll().Where(x => x.UserId == userId).FirstOrDefault();
            if (ou != null)
            {
                var bottom = _organizationUnitRepository.GetAll().Where(x => x.Id == ou.OrganizationUnitId).FirstOrDefault();
                if (bottom != null)
                {
                    var last = bottom;
                    do
                    {
                        lst.Insert(0, last.DisplayName);
                        last = last.Parent;
                    }
                    while (last != null);
                }
            }
            return lst;
        }


        public async Task<List<GetOrganizationCheckedTree>> GetOrganizationUserTree(int taskId, int projectId)
        {
            var message = _pushMessageRepository.GetAll().Where(x => x.ProjectId == projectId && x.TaskId == taskId).Select(x => x.UserId).ToList();

            var list = await _organizationUnitRepository.GetAllListAsync();

            Dictionary<string, GetOrganizationCheckedTree> res = new Dictionary<string, GetOrganizationCheckedTree>();
            foreach (var item in list)
            {
                var userId = _userOrganizationUnitRepository.GetAll().Where(x => x.OrganizationUnitId == item.Id).Select(x => x.UserId);
                var users = UserManager.Users.Where(x => userId.Any(m => m == x.Id)).ToList();
                var id = (item.Id * -1).ToString();
                var org = new GetOrganizationCheckedTree()
                {
                    id = id,
                    text = item.DisplayName,
                    expanded = false,
                    @checked = false,
                    parentId = item.ParentId == null ? "0" : (item.ParentId * -1).ToString(),
                    children = new List<GetOrganizationCheckedTree>(),
                    leaf = false
                    //leaf = list.Any(x => x.ParentId == item.Id) || users.Count > 0 ? false : true
                };

                foreach (var user in users)
                {
                    var dto = new GetOrganizationCheckedTree()
                    {
                        id = user.Id.ToString(),
                        @checked = message.Any(x => x == user.Id),
                        text = user.Name,
                        expanded = true,
                        parentId = id,
                        children = new List<GetOrganizationCheckedTree>(),
                        leaf = true
                    };
                    org.children.Add(dto);
                }
                res.Add(id, org);
            }

            foreach (var item in res.Values)
            {
                if (res.ContainsKey(item.parentId))
                {
                    res[item.parentId].children.Add(item);
                }
            }

            var results = res.Values.Where(x => x.parentId == "0").ToList();

            return results;
        }


        public async Task<List<OrganizationUnitUserListDto>> GetOrganizationUsers(int organizationId)
        {
            var query = from uou in _userOrganizationUnitRepository.GetAll()
                        join ou in _organizationUnitRepository.GetAll() on uou.OrganizationUnitId equals ou.Id
                        join user in UserManager.Users on uou.UserId equals user.Id
                        where uou.OrganizationUnitId == organizationId
                        select new { uou, user };

            var items = await query.ToListAsync();

            return new List<OrganizationUnitUserListDto>(
                items.Select(item =>
                {
                    var dto = item.user.MapTo<OrganizationUnitUserListDto>();
                    dto.AddedTime = item.uou.CreationTime;
                    return dto;
                }).ToList());
        }
    }
}